using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;

namespace Msrp
{
    public class Stack : IStack
    {
        public bool SendMessage(Message f_msg)
        {
            return true;
        }
        public bool Initialize(IMessageProcessor f_msgProcessor, StackConfiguration f_configuration)
        {
            IPEndPoint ep = new IPEndPoint(IPAddress.Any, f_configuration.listenPort);
            m_mainListenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            m_mainListenSocket.Bind(ep);
            m_mainListenSocket.Listen(f_configuration.listeningQueueSize);
            m_mainListenSocket.BeginAccept(new AsyncCallback(AcceptConn), m_mainListenSocket);
            return true;
        }

        /**
         * Creates new MSRP session and connects to the remote host
         * 
         * 
         */
        public bool NewSession(Uri f_localUri, Uri f_remoteUri, bool f_active, Session f_session)
        {
            return true;
        }

        public bool TerminateSession(Uri f_localUri, Uri f_remoteUri)
        {
            return true;
        }


        private void AcceptConn(IAsyncResult iar)
        {
            SocketPacket sp = new SocketPacket();
            Socket oldserver = (Socket)iar.AsyncState;
            Socket newSock = oldserver.EndAccept(iar);
            newSock.
            sp.thisSocket = oldserver.EndAccept(iar);
            sp.thisSocket.BeginReceive(sp.buff.dataBuffer, 0, 2048, SocketFlags.None, new AsyncCallback(ReceiveData), sp);
        }

        private void ReceiveData(IAsyncResult iar)
        {
            SocketPacket sp = (SocketPacket)iar.AsyncState;
            sp.buff.reseivedBytes = sp.thisSocket.EndReceive(iar);
            BufferParser.parseBuffer(ref sp.buff);


            SocketPacket newSp = new SocketPacket();
            // Analyze the parsing result
            if (sp.buff.begginigOfChunkIdx > -1 && sp.buff.endOfChunkIdx > -1) // This complete chunk is in the buffer.
            {
                sp.containingMessageChunk = new MessageChunk();
                sp.containingMessageChunk.addPacket(sp.buff);

                if (sp.buff.endOfChunkIdx < sp.buff.reseivedBytes) // it means we read some data from the next chunk 
                {
                    int bytesToCopy = sp.buff.reseivedBytes - sp.buff.endOfChunkIdx;
                    Array.Copy(sp.buff.dataBuffer, sp.buff.endOfChunkIdx + 1, newSp.buff.dataBuffer, 0, bytesToCopy);
                    newSp.buff.reseivedBytes = bytesToCopy;
                } 
            }

            if (sp.buff.begginigOfChunkIdx > -1 && sp.buff.endOfChunkIdx == -1) // This is the first but not complete packet of the chunk 
            {
                sp.containingMessageChunk = new MessageChunk();
                sp.containingMessageChunk.addPacket(sp.buff);
                newSp.containingMessageChunk = sp.containingMessageChunk;
                newSp.buff.isChunkContinuation = true;
                newSp.buff.startTransactionId = sp.buff.startTransactionId;
            }

            if (sp.buff.begginigOfChunkIdx == -1 && sp.buff.endOfChunkIdx > -1) // This is the last but not first packet of the chunk 
            {
                sp.containingMessageChunk.addPacket(sp.buff);
                if (sp.buff.endOfChunkIdx < sp.buff.reseivedBytes) // it means we read some data from the next chunk 
                {
                    int bytesToCopy = sp.buff.reseivedBytes - sp.buff.endOfChunkIdx;
                    Array.Copy(sp.buff.dataBuffer, sp.buff.endOfChunkIdx + 1, newSp.buff.dataBuffer, 0, bytesToCopy);
                    newSp.buff.reseivedBytes = bytesToCopy;
                }                
                // TODO: add chunk to the message and process message if complete
            }

            if (sp.buff.begginigOfChunkIdx == -1 && sp.buff.endOfChunkIdx == -1) // This is the not first and not last packet of the chunk 
            {
                sp.containingMessageChunk.addPacket(sp.buff);
                newSp.containingMessageChunk = sp.containingMessageChunk;
                newSp.buff.isChunkContinuation = true;
                newSp.buff.startTransactionId = sp.buff.startTransactionId;

            }

            else // This is some serious error here
            {
                // TODO: print log
            }

            newSp.thisSocket = sp.thisSocket;
            newSp.thisSocket.BeginReceive(newSp.buff.dataBuffer, newSp.buff.reseivedBytes + 1, newSp.buff.dataBuffer.Length - (newSp.buff.reseivedBytes + 1), SocketFlags.None, new AsyncCallback(ReceiveData), newSp);
        }

        public class SocketPacket
        {
            public MessageChunk containingMessageChunk = null;
            public Socket thisSocket;
            public BufferParser.Buffer buff = new BufferParser.Buffer();
        };

        private SessionTable m_sessionTable;
        IMessageProcessor m_myMessageProc;
        private Socket m_mainListenSocket;
        private Byte[] buff = new Byte[2048];
    }
}
